package com.bluesky.plum.uimodels.render.html.components.containers.window;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.Queue;

import com.bluesky.plum.uimodels.standard.UIComponent;
import com.bluesky.plum.uimodels.standard.components.containers.window.Dialog;
import com.bluesky.plum.uimodels.standard.components.containers.window.Window;

/**
 * the thread to handle events of the window
 * 
 * @author HZ00260
 * 
 */
public class HWindowThread extends Thread {

	private Window window;
	private Queue<Event> eventQueue;

	private Event currentEvent;

	public HWindowThread(Window window) {
		this.window = window;
		eventQueue = new LinkedList<Event>();

	}

	public void refreshName() {
		this.setName("HTML Window Thread for \"" + window.getTitle() + "\"");
	}

	@Override
	public void run() {
		System.out.println("Window:" + window.getTitle() + " run(): start");
		while (true) {

			System.out.println("Window:" + window.getTitle()
					+ " run(): try to get event");
			Event event = getEvent();
			System.out.println("Window:" + window.getTitle()
					+ " run(): got a event:" + event);
			currentEvent = event;

			// somehow got a null event, WHY? maybe the browser refresh it, then
			// the Servlet thread give a false resume signal.
			if (event == null) {

				continue;
			}

			// recieve a close event
			if (event.getName().equals(Event.Close)) {
				System.out.println("Window:" + window.getTitle()
						+ " run(): got a CLOSE event");
				break;
			}

			// reflect and invoke the method
			try {
				UIComponent uic = window.getChildComponentById(event
						.getComponentId());
				// get the thread of the window. let it handle the event.

				Method m = uic.getClass().getMethod(event.getName(), null);
				m.invoke(uic, null);

				/**
				 * if it is a modal dialog, and the ModalResult is set after the
				 * message has been processed, and it is not visible, it means
				 * the dialog is closing, we should let the parent window thread
				 * proceed and waitting for the last CLOSE event. Otherwise,
				 * then let the servlet thread proceed to render the
				 */

				if (window.isVisible()) {
					/**
					 * notify currentEvent, not event, because after the
					 * m.invoke(), the currentEvent can be reset by child dialog
					 * window thread. @see HDialog.setVisible().
					 */
					synchronized (currentEvent) {

						currentEvent.notify();
						System.out.println("window thread just notify aevent:"
								+ currentEvent);
					}
				}

			} catch (Exception e) {

				System.out.println("Window:" + window.getTitle()
						+ "Error:invoke event handler failed:"
						+ event.getName());
				e.printStackTrace();

				// but it's OK. just ignore it.
				// throw new RuntimeException(e);

				// dont forget let the servlet thread go proceed
				synchronized (event) {
					event.notify();
				}

			}
		}

		// wait

		System.out.println("Window:" + window.getTitle() + " run(): end");
	}

	public synchronized Event getEvent() {
		if (eventQueue.isEmpty()) {
			System.out.println("Window:" + window.getTitle()
					+ " getEvent(): it's empty, gonna wait");
			try {
				wait();// give up the lock, wait at the tail of the queue
				// wait for the producer to get the lock and put goods and
				// notify

			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}

		Event event = eventQueue.poll();
		System.out.println("Window:" + window.getTitle()
				+ " getEvent(): wait finished, got a event:" + event);

		return event;
	}

	public synchronized void putEvent(Event event) {

		System.out.println("Window:" + window.getTitle()
				+ " putEvent(): a event is put:" + event);
		eventQueue.add(event);
		notify();
	}

	public synchronized void putCloseEvent() {
		Event evt = new Event("", Event.Close, null);
		eventQueue.add(evt);
		notify();
	}

	public synchronized void pause() {
		try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public synchronized void proceed() {
		notify();
	}

	public Event getCurrentEvent() {
		return currentEvent;
	}

	public void setCurrentEvent(Event currentEvent) {
		this.currentEvent = currentEvent;
	}

}
